﻿using System;
using System.Collections.Generic;

namespace NJabber.Client
{
    public class PresenceSubscriptionRegistry : IPresenceSubscriptionRegistry
    {
        private Dictionary<JID, PresenceSubscriptionRequest> inboundRequests;
        private Dictionary<JID, PresenceSubscriptionRequest> outboundRequests;
        private IPresenceSubscriptionResponder responder;

        public PresenceSubscriptionRegistry(IPresenceSubscriptionResponder responder)
        {
            if (responder == null) throw new ArgumentNullException("responder");

            this.responder = responder;
            inboundRequests = new Dictionary<JID, PresenceSubscriptionRequest>();
            outboundRequests = new Dictionary<JID, PresenceSubscriptionRequest>();
        }

        public IEnumerable<PresenceSubscriptionRequest> InboundRequests
        {
            get { return inboundRequests.Values; }
        }

        public IEnumerable<PresenceSubscriptionRequest> OutboundRequests
        {
            get { return outboundRequests.Values; }
        }

        public PresenceSubscriptionRequest InboundRequest(JID requestJid)
        {
            if (requestJid == null) throw new ArgumentNullException("requestJid");
            if (!inboundRequests.ContainsKey(requestJid)) throw new InvalidOperationException();
            return inboundRequests[requestJid];
        }

        public PresenceSubscriptionRequest OutboundRequest(JID requestJid)
        {
            if (requestJid == null) throw new ArgumentNullException("requestJid");
            if (!outboundRequests.ContainsKey(requestJid)) throw new InvalidOperationException();
            return outboundRequests[requestJid];
        }

        public void RegisterInboundRequest(JID requestJid)
        {
            PresenceSubscriptionRequest request = AddRequest(inboundRequests, requestJid);
            request.Approved += OnInboundRequestApproved;
            request.Denied += OnInboundRequestDenied;
        }

        public void RegisterOutboundRequest(JID requestJid)
        {
            PresenceSubscriptionRequest request = AddRequest(outboundRequests, requestJid);
            request.Approved += OnOutboundRequestApproved;
            request.Denied += OnOutboundRequestDenied;
        }

        private PresenceSubscriptionRequest AddRequest(IDictionary<JID, PresenceSubscriptionRequest> requests, JID requestJid)
        {
            if (requestJid == null) throw new ArgumentNullException("requestJid");
            PresenceSubscriptionRequest request = new PresenceSubscriptionRequest(requestJid);
            requests[requestJid] = request;
            return request;
        }

        private void OnInboundRequestApproved(object sender, EventArgs e)
        {
            PresenceSubscriptionRequest request = (PresenceSubscriptionRequest) sender;
            responder.SendApproveResponse(request.EntityJid);
            inboundRequests.Remove(request.EntityJid);
            if (InboundRequestApproved != null) InboundRequestApproved(this, new SubscriptionRequestEventArgs(request));
        }

        private void OnInboundRequestDenied(object sender, EventArgs e)
        {
            PresenceSubscriptionRequest request = (PresenceSubscriptionRequest)sender;
            responder.SendDenyResponse(request.EntityJid);
            inboundRequests.Remove(request.EntityJid);
            if (InboundRequestDenied != null) InboundRequestDenied(this, new SubscriptionRequestEventArgs(request));
        }

        private void OnOutboundRequestApproved(object sender, EventArgs e)
        {
            PresenceSubscriptionRequest request = (PresenceSubscriptionRequest)sender;
            outboundRequests.Remove(request.EntityJid);
            if (OutboundRequestApproved != null) OutboundRequestApproved(this, new SubscriptionRequestEventArgs(request));
        }

        private void OnOutboundRequestDenied(object sender, EventArgs e)
        {
            PresenceSubscriptionRequest request = (PresenceSubscriptionRequest)sender;
            outboundRequests.Remove(request.EntityJid);
            if (OutboundRequestDenied != null) OutboundRequestDenied(this, new SubscriptionRequestEventArgs(request));
        }

        public event EventHandler<SubscriptionRequestEventArgs> InboundRequestApproved;
        public event EventHandler<SubscriptionRequestEventArgs> InboundRequestDenied;
        public event EventHandler<SubscriptionRequestEventArgs> OutboundRequestApproved;
        public event EventHandler<SubscriptionRequestEventArgs> OutboundRequestDenied;
    }
}